﻿using System;
using System.Collections.Generic;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace DeepCompare.Tests
{
    /// <summary>
    ///     Test fixture for <see cref="DeepComparer" />.
    /// </summary>
    [TestClass]
    public class UnitTestDeepComparerImplementation : DeepComparer
    {
        [TestMethod]
        public void TestAssertStatement()
        {
            Assert.IsTrue(AssertIsTrue(() => true,
                ""));
        }

        [TestMethod]
        public void TestCheckNull()
        {
            Reinitialize();
            Assert.IsTrue(ContinueAfterCheckIfOneIsNullAndTheOtherNot(new object(),
                new object()));
            Reinitialize();
            Assert.IsFalse(ContinueAfterCheckIfOneIsNullAndTheOtherNot(default(object),
                new object()));
            Reinitialize();
            Assert.IsFalse(ContinueAfterCheckIfOneIsNullAndTheOtherNot(new object(),
                default(object)));
        }

        [TestMethod]
        public void TestContinueAfterCheckWithOwnEqualMethod()
        {
            Reinitialize();
            Assert.IsFalse(ContinueAfterCheckWithOwnEqualMethod(new TestClassWithOwnEquality(),
                new TestClassWithOwnEquality()));
            Assert.IsTrue(Differences.Count == 0);
        }

        [TestMethod]
        public void TestCheckUsingStandardTypes()
        {
            Assert.IsTrue(CheckUsingStandardTypesSuccess(1,
                1));
        }

        [TestMethod]
        public void TestCheckArraysAndLists()
        {
            // Check if this are arrays
            Reinitialize();
            Assert.IsTrue(CheckArraysAndListsWasDone(new int[]
            {
            },
                new int[]
                {
                }));
            Assert.IsTrue(Differences.Count == 0);
            // Check if all is fine
            Reinitialize();
            Assert.IsTrue(CheckArraysAndListsWasDone(new[]
            {
                1, 2, 3
            },
                new[]
                {
                    1, 2, 3
                }));
            Assert.IsTrue(Differences.Count == 0);
            // Check array with list in correct state
            Reinitialize();
            Assert.IsTrue(CheckArraysAndListsWasDone(new[]
            {
                1, 2, 3
            },
                new List<int>
                {
                    1, 2, 3
                }));
            Assert.IsTrue(Differences.Count == 0);
            // Check if the count was not correct
            Reinitialize();
            Assert.IsTrue(CheckArraysAndListsWasDone(new[]
            {
                1, 2, 3
            },
                new[]
                {
                    1, 2
                }));
            Assert.IsTrue(Differences.Count == 1);
            Assert.AreEqual(ERR_DIFFERENT_ELEMENT_COUNT,
                Differences[0].Message);
        }

        [TestMethod]
        public void CheckGetAllMemberNames()
        {
            Assert.AreEqual(6,
                GetAllMemberNames(new TestClassOne()).Count);
        }

        [TestMethod]
        public void IntegrationCheckLevelOne()
        {
            // Compare with 2 different classes with different field/property signature but with the same content.
            Assert.IsFalse(
                Compare(new TestClassOne
                {
                    Lastname = "Baarz"
                },
                    new TestClassTwo
                    {
                        Lastname = "Baarz1"
                    }));
            Assert.AreEqual(2,
                Differences.Count);
            // Compare with 2 different classes with different field/property signature but with the same content.
            Assert.IsFalse(
                Compare(new TestClassOne
                {
                    Zip = 13405
                },
                    new TestClassTwo
                    {
                        Zip = 03046
                    }));
            Assert.AreEqual(2,
                Differences.Count);
            // All is fine with 2 different classes where one is not from the same signature
            TreatStructureDifferencesOnNullPropertiesAsWarnings = true;
            Assert.IsTrue(
                Compare(
                    new TestClassOne
                    {
                        Lastname = "Baarz"
                    },
                    new TestClassTwo
                    {
                        Lastname = "Baarz"
                    }));
            TreatStructureDifferencesOnNullPropertiesAsWarnings = false;
        }

        [TestMethod]
        public void CheckUntypedVariables()
        {
            // Equal objects
            Assert.IsTrue(Compare(
                new
                {
                    Name = "Baarz",
                    ArrayProperty = new[]
                    {
                        1, 2, 3, 4
                    }
                },
                new
                {
                    Name = "Baarz",
                    ArrayProperty = new[]
                    {
                        1, 2, 3, 4
                    }
                }));
            // Unequal property
            Assert.IsFalse(Compare(
                new
                {
                    Firstname = "Michael"
                },
                new
                {
                    Firstname = "Damon"
                }));
        }

        [TestMethod]
        public void IntegrationCheckWithNestedLists()
        {
            TreatStructureDifferencesOnNullPropertiesAsWarnings = true;
            // Internal list where members are equal
            Assert.IsTrue(Compare(new TestClassOne
            {
                ClassThreeList = new[]
                {
                    new TestClassThree()
                }
            },
                new TestClassTwo
                {
                    ClassThreeList = new List<TestClassThree>
                    {
                        new TestClassThree()
                    }
                }));
            // Different count of list entries
            Assert.IsFalse(Compare(new TestClassOne
            {
                ClassThreeList = new[]
                {
                    new TestClassThree(), new TestClassThree(),
                }
            },
                new TestClassTwo
                {
                    ClassThreeList = new List<TestClassThree>
                    {
                        new TestClassThree()
                    }
                }));
            Assert.AreEqual(1,
                Differences.Count);
            // Same count of entries but with different property
            Assert.IsFalse(Compare(new TestClassOne
            {
                ClassThreeList = new[]
                {
                    new TestClassThree
                    {
                        Name = "Baarz"
                    }
                }
            },
                new TestClassTwo
                {
                    ClassThreeList = new List<TestClassThree>
                    {
                        new TestClassThree()
                    }
                }));
            Assert.AreEqual(1,
                Differences.Count);
            TreatStructureDifferencesOnNullPropertiesAsWarnings = false;
        }

        [TestMethod]
        public void CheckDynamic()
        {
            // Equal objects
            dynamic objectOne = new
            {
                Name = "Baarz",
                ArrayProperty = new[]
                {
                    1, 2, 3, 4
                }
            };
            dynamic objectTwo = new
            {
                Name = "Baarz",
                ArrayProperty = new[]
                {
                    1, 2, 3, 4
                }
            };
            Assert.IsTrue(Compare(
                objectOne,
                objectTwo));
            // Unequal property
            objectOne = new
            {
                Firstname = "Michael"
            };
            objectTwo = new
            {
                Firstname = "Damon"
            };
            Assert.IsFalse(Compare(
                objectOne,
                objectTwo));
        }

        [TestMethod]
        public void CheckTreatStructureDifferencesOnNullPropertiesAsWarnings()
        {
            // Without treating as warnings
            var comparer = new DeepComparer();
            Assert.IsFalse(comparer.Compare(new TestClassOne(),
                new TestClassThree()));
            Assert.AreEqual(6,
                comparer.GetDifferences().GetLength(0));
            Assert.IsFalse(comparer.HasWarnings());
            // Now check with treat warnings
            comparer.TreatStructureDifferencesOnNullPropertiesAsWarnings = true;
            Assert.IsFalse(comparer.Compare(new TestClassOne(),
                new TestClassThree()));
            Assert.AreEqual(1,
                comparer.GetDifferences().GetLength(0));
            Assert.AreEqual(5,
                comparer.GetWarnings().GetLength(0));
        }

        [TestMethod]
        public void IntegrationTestForStandardTypes()
        {
            var comparer = new DeepComparer();
            Assert.IsTrue(comparer.Compare((short)1,
                (short)1));
            Assert.IsTrue(comparer.Compare((int)1,
                (int)1));
            Assert.IsTrue(comparer.Compare((long)1,
                (long)1));
            Assert.IsTrue(comparer.Compare((uint)1,
                (uint)1));
            Assert.IsTrue(comparer.Compare((ushort)1,
                (ushort)1));
            Assert.IsTrue(comparer.Compare((ulong)1,
                (ulong)1));
            Assert.IsTrue(comparer.Compare("test",
                "test"));
            Assert.IsTrue(comparer.Compare(true,
                true));
            Assert.IsTrue(comparer.Compare('d',
                'd'));
            Assert.IsTrue(comparer.Compare((byte)1,
                (byte)1));
            Assert.IsTrue(comparer.Compare((decimal)1,
                (decimal)1));
            Assert.IsTrue(comparer.Compare((double)1,
                (double)1));
            var guid = Guid.NewGuid();
            Assert.IsTrue(comparer.Compare(guid,
                Guid.Parse(guid.ToString())));
            Assert.IsTrue(comparer.Compare((SByte)2,
                (SByte)2));
            Assert.IsTrue(comparer.Compare((Single)1,
                (Single)1));
            Assert.IsTrue(comparer.Compare(TimeSpan.FromDays(1),
                TimeSpan.FromDays(1)));
        }

        [TestMethod]
        public void CheckEnumMemberComparision()
        {
            var comparer = new DeepComparer(true);
            Assert.IsTrue(comparer.Compare(
                new TestClassOne
                {
                    Side = ComparerSide.RightHand,
                    Sides = new[] { ComparerSide.LeftHand, ComparerSide.RightHand, }
                },
                new TestClassTwo
                {
                    Side = ComparerSide.RightHand,
                    Sides = new List<ComparerSide> { ComparerSide.LeftHand, ComparerSide.RightHand, }
                }));
            Assert.IsFalse(comparer.Compare(
                new TestClassOne
                {
                    Side = ComparerSide.RightHand,
                    Sides = new[] { ComparerSide.LeftHand }
                },
                new TestClassTwo
                {
                    Side = ComparerSide.RightHand,
                    Sides = new List<ComparerSide> { ComparerSide.LeftHand, ComparerSide.RightHand, }
                }));
        }
    }
}